/**
 * Módulo para trabalhar com listas ligadas
 *
 */

#include <stdlib.h>
#include <stdio.h>

typedef struct lligada_nodo {
	void *info;
	struct lligada_nodo *prox;
}LLIGADA_NODO;


#include "lista_ligada.h"


/**
 * @fn lligada_inserir
 * @brief Funcao que insere numa lista ligada
 * @param lista Lista a inserir
 * @param info Informacao a inserir na lista
 * @return Apontador para a cabeca da lista em sucesso, NULL em contrario
 */
int lligada_inserir (LLIGADA *lista, void *info)	{

	LLIGADA aux = (LLIGADA ) malloc(sizeof(LLIGADA_NODO));
	if(aux == NULL) return (-1);
	aux->info = info;
	aux->prox = (*lista);

	(*lista) = aux;

	return 0;
}

/**
 * @fn lligada_pesquisar
 * @brief Função de pesquisa numa lista ligada
 * @param lista lista a procurar a informação
 * @param chave apontador para
 * @param funcao_comparacao funcao que verifica se uma chave corresponde a uma informação, retorna 1 em verdadeiro
 * @return *info encontrar a informação correspondente a chave, NULL em caso contrario
 */
void * lligada_pesquisar (LLIGADA lista, void *chave, int (*funcao_comparacao)(void *,void *))	{

	LLIGADA aux = lista;

	for(aux = lista; aux ; aux = aux->prox)
		if((*funcao_comparacao)(chave,aux->info) == 1)
			return (aux->info);

	return NULL;
}

/**
 * @fn lligada_remover
 * @brief Funcao que elimina um elemento da lista.
 * @param lista lista ligada para eliminar o elemento
 * @param chave chave da informação para remover
 * @param funcao_comparacao funcao que verifica se uma chave corresponde a uma informação, retorna 1 em verdadeiro
 * @return 0 em sucesso, 1 caso o elemento nao exista
 */
int lligada_remover (LLIGADA *lista, void *chave, int (*funcao_comparacao)(void *,void *))	{

	LLIGADA aux,aux2;

	if(*lista 		== NULL) return 1;
	if((*funcao_comparacao)(chave,(*lista)->info) == 1) {
		aux=*lista;
		(*lista)	=	(*lista)->prox;
		free(aux);
	}
	for(aux = *lista; aux->prox ; aux = aux->prox)
		if((*funcao_comparacao)(chave,aux->prox->info) == 1) {

			aux2=aux->prox->prox;
			free(aux->prox->info);
			free(aux->prox);
			aux->prox = aux2;
			return 0;
		}

	return 0;
}

/**
 * @fn lligada_map
 * @brief Funcao que aplica uma dada funcao a todos os elementos da lista //map em Haskell
 * @param lista é a lista a aplicar a funcao em todos os elementos
 * @param f é a funcao a aplicar, deve retornar 0 em sucesso, e 1 noutros casos
 * @return o numero de operacoes sem sucesso
 */
int lligada_map (LLIGADA lista, int (*f)(void *, void *params), void *params)	{

	LLIGADA aux = lista;
	int result=0;

	for( aux = lista ; aux ; aux = aux->prox ) result += (*f)(aux->info, params);

	return result;
}

/**
 * Funcao que devolte o tamanho de uma lista
 */
int lligada_tamanho(LLIGADA lista, int *tamanho)	{

	LLIGADA aux;
	*tamanho =0;

	for( aux = lista ; aux ; aux=aux->prox ) tamanho++;

	return *tamanho;
}

/**
 * Funcao que recebe uma lista e cria um array com os seus componentes
 */
void *lligada_para_arr (LLIGADA lista, int *tm)	{

	int tamanho;
	lligada_tamanho(lista, &tamanho);

	void **arr = (void **) calloc(tamanho + 1,sizeof(void **));
	LLIGADA aux;
	int i;

	for ( i=0 , aux = lista ; aux ; i++ , aux=aux->prox ) {
		arr[i] = aux->info;
		//printf("%c %d %d", arr[i]->tipo, arr[i]->peso, arr[i]->chave);
	}
	*tm = i;
	return *arr;
}